package com.gviiii.baiduai.file;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.google.common.collect.Sets;
import com.gviiii.baiduai.cache.Cache;
import com.gviiii.baiduai.cache.FileCache;
import com.gviiii.baiduai.file.monitor.FileMonitor;
import com.gviiii.baiduai.pojo.FileInfo;
import com.gviiii.baiduai.util.FileUtil;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 获取图片，以及配置
 */
public class FileScanner {

    private static final Log log = LogFactory.get();

    private volatile String rootDir;

    private volatile String scannerName;

    private volatile FileCache<String, FileInfo> oldCache;

    public FileScanner(String rootDir, String scannerName) {
        this.rootDir = rootDir;
        this.scannerName = scannerName;
    }

    /**
     * 获取待识别图片，以及图片识别后剪切输出目录
     */
    @SuppressWarnings("unchecked")
    public Cache<String, FileInfo> start() {
        Set<Path> pathSet = scanFile(rootDir);
        this.oldCache = FileCache.getFileCache(scannerName, 100);
        for (Path path : pathSet) {
            oldCache.put(path.toString(), addFileInfo(path));
        }
        startMonitor();
        return oldCache;
    }

    public void stop() {
        oldCache.clear();
    }

    /**
     * 初始化文件监控线程
     */
    public void startMonitor() {
        FileMonitor alterationFileMonitor = new FileMonitor("FileMonitor-alteration-"+scannerName+"-");
        alterationFileMonitor.scheduleAtFixedRate(new AlterationFileMonitor(), 1, 5, TimeUnit.SECONDS);
    }

    /**
     * 获取文件路径集合
     *
     * @param pathname
     * @return
     */
    private Set<Path> scanFile(String pathname) {
        Set<Path> pathSet = null;
        try {
            Stream<Path> listFile = Files.list(Paths.get(FileUtil.updateFilePath(pathname)));
            pathSet = listFile.collect(Collectors.toSet());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return pathSet;
    }

    /**
     * 与缓存中的文件路径比较
     *
     * @param newPathSet
     * @param oldCache
     * @return
     */
    private void compareFile(Set<Path> newPathSet, FileCache<String, FileInfo> oldCache) {
        Set<Path> oldPathSet = Sets.newLinkedHashSet();
        oldCache.keys().forEach(s -> oldPathSet.add(Paths.get(s)));
        if (oldPathSet.isEmpty()) {
            addFile(newPathSet);
        } else {
            //找到新增文件路径
            if (newPathSet.size() - oldPathSet.size() > 0) {
                Set<Path> pathSet = new HashSet<>();
                swapSetData(oldPathSet, newPathSet, pathSet);
                addFile(pathSet);
            } else if (newPathSet.size() - oldPathSet.size() == 0) {
                Set<Path> pathSet = new HashSet<>();
                //将文件修改前的信息移除
                swapSetData(newPathSet, oldPathSet, pathSet);
                if (pathSet.size() > 0) {
                    pathSet.forEach(path -> {
                        oldPathSet.remove(path);
                        oldCache.remove(path.toString());
                        log.info("修改前的文件:" + path, Level.INFO);
                    });

                    pathSet.clear();
                    //将修改后的文件信息添加
                    swapSetData(oldPathSet, newPathSet, pathSet);
                    pathSet.forEach(path -> {
                        oldPathSet.add(path);
                        oldCache.put(path.toString(),addFileInfo(path));
                        log.info("修改后的文件:" + path, Level.INFO);
                    });
                    pathSet.clear();
                }
            } else {
                Set<Path> pathSet = new HashSet<>();
                //找到不存在文件路径
                swapSetData(newPathSet, oldPathSet, pathSet);
                removeFile(pathSet);
            }
        }
    }

    /**
     * 找到变更的文件
     *
     * @param sourceSet
     * @param targetSet
     * @param tempSet
     */
    private void swapSetData(Set<Path> sourceSet, Set<Path> targetSet, Set<Path> tempSet) {
        tempSet.addAll(targetSet);
        tempSet.removeAll(sourceSet);
        if (tempSet.size() > 0) {
            log.info(tempSet.toString());
        }
    }

    /**
     * 添加文件到缓存
     *
     * @param pathSet
     */
    public void addFile(Set<Path> pathSet) {
        pathSet.forEach(path -> {
            oldCache.put(path.toString(), addFileInfo(path));
            log.info("新增文件:" + path, Level.INFO);
        });
    }

    /**
     * 从缓存移除文件
     *
     * @param pathSet
     */
    public void removeFile(Set<Path> pathSet) {
        pathSet.forEach(path -> {
            oldCache.remove(path.toString());
            log.info("移除文件" + path, Level.INFO);
        });
    }

    /**
     * 添加文件信息
     *
     * @param path
     * @return
     */
    private FileInfo addFileInfo(Path path) {
        String pathStr = path.toString();
        FileInfo fileInfo = new FileInfo();
        fileInfo.setFileName(FileUtil.getSimpleFileName(path.getFileName().toString()));
        fileInfo.setFileExName(FileUtil.getSimpleFileExName(pathStr));
        fileInfo.setFilePath(pathStr);
        fileInfo.setFileEncode(FileUtil.encodeFiles(pathStr));
        fileInfo.setTime(LocalDateTime.now());
        return fileInfo;
    }

    public FileCache<String, FileInfo> getOldCache() {
        return oldCache;
    }

    /**
     * 文件变化监听线程
     */
    private class AlterationFileMonitor implements Runnable {

        public AlterationFileMonitor() {
        }

        @Override
        public void run() {
            compareFile(scanFile(rootDir), oldCache);
        }
    }
}